package cn.zbacm.api.security.service;

import cn.zbacm.api.application.entity.Application;
import cn.zbacm.api.application.entity.ApplicationRole;
import cn.zbacm.api.application.repository.ApplicationRepository;
import cn.zbacm.api.application.repository.ApplicationRoleRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;

import java.io.Serializable;
import java.util.List;
import java.util.stream.Collectors;

@Component
public class CustomPermissionEvaluator implements PermissionEvaluator {
  @Autowired
  private ApplicationRepository applicationRepository;

  @Autowired
  private ApplicationRoleRepository applicationRoleRepository;

  /**
   * 接口可管理多个应用，用户可拥有多个角色（从而拥有多个应用的权限），此方法取两个应用集的交集，判断交集中的权限是否满足接口要求
   *
   * @param auth               用户身份
   * @param targetDomainObject 接口标识
   * @param permission         读写权限
   * @return 检查结果
   */
  @Override
  public boolean hasPermission(Authentication auth, Object targetDomainObject, Object permission) {
    if ((auth.getName().equals("anonymous"))
            || !(targetDomainObject instanceof String)
            || !(permission instanceof Integer)) {
      return false;
    }

    List<Integer> apiAppIds = getApiAppIds((String) targetDomainObject);
    List<Integer> roleIds;
    try {
      roleIds =
              auth.getAuthorities().stream()
                      .map(authority -> Integer.parseInt(authority.getAuthority()))
                      .collect(Collectors.toList());
    } catch (NumberFormatException err) {
      return false;
    }

    List<ApplicationRole> userAppRoles =
            applicationRoleRepository.findByApplicationIdInAndRoleIdIn(apiAppIds, roleIds);

    for (ApplicationRole userAppRole : userAppRoles) {
      if (userAppRole.getPrivilege() >= (Integer) permission) {
        return true;
      }
    }
    return false;
  }

  /**
   * 当用户使用 ID 请求入口（ta 的根节点）时，检查 ID 是否属于该用户，可选是否检查权限
   *
   * @param auth       用户身份
   * @param targetId   根节点参数
   * @param targetType 接口标识
   * @param permission 读写权限（仅当不为 null 时检查）
   * @return 检查结果
   */
  @Override
  public boolean hasPermission(
          Authentication auth, Serializable targetId, String targetType, Object permission) {
    if ((auth.getName().equals("anonymous"))
            || (targetId == null)
            || (targetType == null)
            || (permission != null && !(permission instanceof Integer))) {
      return false;
    }

    List<Integer> apiAppIds = getApiAppIds(targetType);
    List<Integer> roleIds;
    try {
      roleIds =
              auth.getAuthorities().stream()
                      .map(authority -> Integer.parseInt(authority.getAuthority()))
              .collect(Collectors.toList());
    } catch (NumberFormatException err) {
      return false;
    }
    List<ApplicationRole> userAppRoles =
            applicationRoleRepository.findByApplicationIdInAndRoleIdIn(apiAppIds, roleIds);

    for (ApplicationRole userAppRole : userAppRoles) {
      if (userAppRole.getParameter().equals(targetId.toString())) {
        if (permission == null) {
          return true;
        }
        if (userAppRole.getPrivilege() >= (Integer) permission) {
          return true;
        }
      }
    }
    return false;
  }

  // 获取接口的应用集
  // 一般接口只有一个应用
  private List<Integer> getApiAppIds(String privilegeCode) {
    List<Application> applications = applicationRepository.findByPrivilegeCode(privilegeCode);
    return applications.stream().map(Application::getId).collect(Collectors.toList());
  }
}
